<template>
  <div class="app-container">
    <div class="engine-tabs">
      <el-tabs v-model="activeEngine" @tab-click="handleEngineChange">
        <el-tab-pane label="OCR引擎1" name="engine1"></el-tab-pane>
        <el-tab-pane label="OCR引擎2" name="engine2"></el-tab-pane>
        <el-tab-pane label="OCR引擎3" name="engine3"></el-tab-pane>
      </el-tabs>
    </div>
    
    <div class="ocr-container">
      <!-- 上方API接口调用方法说明 -->
      <div class="api-doc-section">
        <h1>API 接口调用方法说明</h1>
        <h2>接口地址</h2>
        <p v-if="activeEngine === 'engine1'">服务器A：<code>{{ urlA }}:5050/api/image_to_ocr</code></p>
        <p v-if="activeEngine === 'engine2'">服务器B：<code>{{ urlB }}:5050/api/image_to_ocr</code></p>
        <p v-if="activeEngine === 'engine3'">服务器C：<code>{{ urlC }}:5050/api/image_to_ocr</code></p>
        
        <h2>请求方法</h2>
        <p><code>POST</code></p>
        
        <h2>请求头</h2>
        <pre>Content-Type: multipart/form-data</pre>
        
        <h2>请求体</h2>
        <pre>{
  "file": &lt;file&gt;  // 支持.jpg, .jpeg, .png, .bmp, .tif, .pdf格式
}</pre>
        
        <h2>响应示例</h2>
        <pre>{
  "json_results": [
    {
      "height": 20,
      "width": 10,
      "str": "A",
      "y": 100,
      "x": 50,
      "accumulated_y": 100
    },
    ...
  ],
  "final_text_output": "这是识别的文本结果，换行信息根据原始文本中的方块，如需按自然段等换行需调用接口时对/n进行处理。上面的文字定位JSON信息为适配自动标引，以单个字为基本单位。"
}</pre>

        <h2>CURL 命令示例</h2>
        <pre>curl -X POST {{ currentUrl }}:5050/api/image_to_ocr \
-F "file=@/path/to/your/file.jpg"</pre>
        <div class="note">请将 <code>/path/to/your/file.jpg</code> 替换为您要上传的文件的实际路径。</div>
      </div>
      
      <!-- 下方OCR使用界面 -->
      <div class="ocr-usage-section">
        <h1>{{ currentTxt }}</h1>
        <div class="upload-section">
          <el-upload
            class="upload-box"
            drag
            action="#"
            :auto-upload="false"
            :show-file-list="false"
            :on-change="handleFileChange"
            :before-upload="beforeUpload"
            accept="image/jpeg,image/jpg,image/png,image/bmp,image/tiff,application/pdf">
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">将图片拖到此处，或<em>点击上传</em></div>
            <div class="el-upload__tip" slot="tip">
              支持格式：jpg, jpeg, png, bmp, tif, tiff, 文件大小不超过10MB
            </div>
          </el-upload>
          <el-button 
            type="primary" 
            @click="uploadImage" 
            :disabled="!imageFile" 
            :loading="loading"
            style="margin-top: 20px">
            开始识别
          </el-button>
        </div>
        
        <div v-if="previewUrl" class="result-section">
          <!-- OCR识别结果展示 -->
          <div class="result-title">OCR识别结果：</div>
          
          <!-- 快速调整按钮 - 布局优化 -->
          <div class="adjust-controls">
            <div class="quick-adjust">
              <el-button type="primary" size="small" @click="quickAdjustRight(-20)">向左 -20px</el-button>
              <el-button type="primary" size="small" @click="quickAdjustRight(20)">向右 +20px</el-button>
              <el-button type="primary" size="small" @click="quickAdjustRight(50)">向右 +50px</el-button>
              <el-button type="warning" size="small" @click="resetAdjustments">重置</el-button>
              <el-button 
                type="success" 
                size="small"
                @click="toggleTableMode" 
                :class="{'is-active': tableMode}">
                {{ tableMode ? '退出表格模式' : '表格模式' }}
              </el-button>
            </div>
            
            <!-- 文字位置调整选项 -->
            <div class="adjust-options">
              <div class="adjust-item">
                <span>水平位置调整: {{ textOffsetX }}px</span>
                <el-slider 
                  v-model="textOffsetX"
                  :min="-50"
                  :max="200"
                  :step="5"
                  @change="reprocessOcrResults">
                </el-slider>
              </div>
              <div class="adjust-item">
                <span>垂直位置调整: {{ textOffsetY }}px</span>
                <el-slider 
                  v-model="textOffsetY"
                  :min="-50"
                  :max="50"
                  @change="reprocessOcrResults">
                </el-slider>
              </div>
              <div class="adjust-item">
                <span>背景透明度: {{ bgOpacity }}</span>
                <el-slider 
                  v-model="bgOpacity"
                  :min="0.2"
                  :max="0.9"
                  :step="0.1"
                  @change="updateTextStyle">
                </el-slider>
              </div>
              <div class="adjust-item">
                <span>字体大小调整: {{ fontSizeAdjust }}px</span>
                <el-slider 
                  v-model="fontSizeAdjust"
                  :min="-5"
                  :max="10"
                  :step="1"
                  @change="reprocessOcrResults">
                </el-slider>
              </div>
            </div>
          </div>
          
          <!-- 放大图片显示区域 -->
          <div class="image-preview-section">
            <div class="image-container ocr-result-container" :class="{'table-mode': tableMode}">
              <img :src="previewUrl" class="ocr-image" />
              <div class="ocr-text-layer">
                <div 
                  v-for="(item, index) in ocrResult" 
                  :key="index" 
                  class="ocr-text-item"
                  :class="{'table-cell': tableMode}"
                  :style="{
                    left: item.x + 'px',
                    top: item.y + 'px',
                    width: item.width + 'px',
                    height: item.height + 'px',
                    fontSize: item.fontSize
                  }">
                  {{ item.str }}
                </div>
              </div>
            </div>
          </div>
          
          <!-- 完整识别文本 -->
          <div v-if="textOutput" class="full-text-section">
            <div class="result-title">完整识别文本：</div>
            <el-input
              type="textarea"
              :rows="8"
              placeholder="识别结果将在这里显示"
              v-model="textOutput"
              readonly>
            </el-input>
            <!-- 添加复制按钮 -->
            <el-button 
              type="primary" 
              size="mini" 
              style="margin-top: 10px"
              @click="copyTextToClipboard">
              复制文本
            </el-button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import request from '@/utils/request'
import Tiff from 'tiff.js' // 导入tiff.js库

export default {
  data() {
    return {
      activeEngine: 'engine1',
      urlA: process.env.VUE_APP_BASE_SER_HOST_A,
      urlB: process.env.VUE_APP_BASE_SER_HOST_B,
      urlC: process.env.VUE_APP_BASE_SER_HOST_C,
      
      // OCR使用相关数据
      imageFile: null,
      previewUrl: '',
      ocrResult: [],
      textOutput: '',
      loading: false,
      imageWidth: 0,
      imageHeight: 0,
      displayWidth: 0,
      displayHeight: 0,
      // 文字位置调整参数
      textOffsetX: 20,  // 水平方向额外偏移量，默认向右偏移20px
      textOffsetY: 0,   // 垂直方向额外偏移量
      originalOcrResults: [],
      bgOpacity: 0.5, // 背景透明度默认值调小
      fontSizeAdjust: 0, // 字体大小额外调整值，默认改为0
      tableMode: false, // 表格模式标志
      isTiffImage: false // 标记是否为tiff格式图片
    }
  },
  computed: {
    currentUrl() {
      if (this.activeEngine === 'engine1') return this.urlA
      if (this.activeEngine === 'engine2') return this.urlB
      if (this.activeEngine === 'engine3') return this.urlC
      return this.urlA
    },


    currentTxt() {
      if (this.activeEngine === 'engine1') return  "OCR识别引擎1测试"
      if (this.activeEngine === 'engine2') return "OCR识别引擎2测试"
      if (this.activeEngine === 'engine3') return "OCR识别引擎3测试"
 
    },

    

    apiEndpoint() {
      return `${this.currentUrl}:5050/api/image_to_ocr`
    }
  },
  methods: {
    handleEngineChange(tab) {
      this.activeEngine = tab.name
      // 清除之前的OCR结果
      this.resetOcrResults()
    },
    resetOcrResults() {
      this.imageFile = null
      this.previewUrl = ''
      this.ocrResult = []
      this.textOutput = ''
      this.originalOcrResults = []
      this.isTiffImage = false
    },
    beforeUpload(file) {
      // 检查文件类型
      const fileExtension = file.name.toLowerCase().split('.').pop();
      const validExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'tif', 'tiff'];
      const isValidType = validExtensions.includes(fileExtension) || 
                         file.type.indexOf('image/') === 0 || 
                         file.type === 'application/pdf';
      const isLimitSize = file.size / 1024 / 1024 < 10;
      
      if (!isValidType) {
        this.$message.error('只能上传图片(jpg/jpeg/png/bmp/tif/tiff)或PDF文件!');
      }
      if (!isLimitSize) {
        this.$message.error('文件大小不能超过 10MB!');
      }
      
      return isValidType && isLimitSize;
    },
    handleFileChange(file) {
      if (file) {
        this.imageFile = file.raw;
        const fileExtension = file.name.toLowerCase().split('.').pop();
        
        // 检查是否为tiff格式
        if (fileExtension === 'tif' || fileExtension === 'tiff') {
          this.isTiffImage = true;
          this.handleTiffImage(file.raw);
        } else {
          this.isTiffImage = false;
          this.previewUrl = URL.createObjectURL(this.imageFile);
          
          // 获取图片尺寸
          this.getImageDimensions(this.previewUrl);
        }
        
        // 清除之前的OCR结果
        this.ocrResult = []
      }
    },
    // 处理tiff格式图片
    handleTiffImage(tiffFile) {
      const reader = new FileReader();
      
      reader.onload = (e) => {
        try {
          // 使用tiff.js解析tiff文件
          const tiff = new Tiff({ buffer: e.target.result });
          
          // 获取第一帧图像的宽高
          this.imageWidth = tiff.width();
          this.imageHeight = tiff.height();
          
          // 将tiff转换为canvas，再转换为base64数据URL
          const canvas = tiff.toCanvas();
          this.previewUrl = canvas.toDataURL('image/png');
          
          // 释放内存
          tiff.close();
        } catch (error) {
          console.error('处理TIFF图片出错:', error);
          this.$message.error('无法处理此TIFF图片，请尝试其他格式');
        }
      };
      
      reader.onerror = () => {
        this.$message.error('读取TIFF文件失败');
      };
      
      // 以ArrayBuffer格式读取文件
      reader.readAsArrayBuffer(tiffFile);
    },
    // 获取图片尺寸
    getImageDimensions(imageUrl) {
      const img = new Image();
      img.onload = () => {
        this.imageWidth = img.width;
        this.imageHeight = img.height;
      };
      img.src = imageUrl;
    },
    uploadImage() {
      if (!this.imageFile) {
        this.$message.warning('请先选择图片')
        return
      }
      
      this.loading = true
      
      // 创建FormData对象
      const formData = new FormData()
      formData.append('file', this.imageFile)
      
      // 使用request而不是$axios发送请求到OCR API
      request({
        // url: this.apiEndpoint,
         url: 'http://172.20.10.2:5051/api/image_to_ocr',
        method: 'post',
        data: formData,
        headers: {
          'Content-Type': 'multipart/form-data'
        }
      })
      .then(response => {
        this.loading = false
        const data = response
        
        
        if (data && data.json_results) {
          this.ocrResult = []
          this.textOutput = data.text_output || ''
          
          // 保存原始OCR结果
          this.originalOcrResults = JSON.parse(JSON.stringify(data.json_results))
          
          // 等待DOM更新完成后再调整位置
          this.$nextTick(() => {
            // 先获取图片的显示尺寸，然后处理OCR结果
            this.getImageDisplaySize().then(() => {
              // 处理OCR结果
              this.processOcrResults(this.originalOcrResults)
            })
          })
        } else {
          this.$message.error('OCR识别失败，请重试')
        }
      })
      .catch(error => {
        this.loading = false
        console.error('OCR识别出错:', error)
        this.$message.error('OCR服务请求出错，请检查网络连接')
      })
    },
    getImageDisplaySize() {
      return new Promise((resolve) => {
        // 等待图片加载完成并获取显示尺寸
        const checkImage = () => {
          const imgElement = document.querySelector('.ocr-image')
          if (imgElement && imgElement.complete) {
            this.displayWidth = imgElement.clientWidth
            this.displayHeight = imgElement.clientHeight
            resolve()
          } else {
            setTimeout(checkImage, 100) // 等待100ms后再次检查
          }
        }
        checkImage()
      })
    },
    processOcrResults(results) {
      if (!results || !results.length) return
      
      console.log('原始图片尺寸:', this.imageWidth, this.imageHeight)
      console.log('显示尺寸:', this.displayWidth, this.displayHeight)
      
      // 清空当前结果
      this.ocrResult = []
      
      // 计算缩放比例
      const scaleX = this.displayWidth / this.imageWidth
      const scaleY = this.displayHeight / this.imageHeight
      
      // 使用同样的缩放比例
      const scale = Math.min(scaleX, scaleY)
      
      // 计算居中偏移
      const offsetX = (this.displayWidth - this.imageWidth * scale) / 2
      const offsetY = (this.displayHeight - this.imageHeight * scale) / 2
      
      // 调整OCR结果的位置和大小
      results.forEach(item => {
        // 减小字体大小系数和最小值：由80%改为50%，最小值从16px改为10px
        // 根据高度计算合适的字体大小，约为高度的50%，并且最小字体设为10px
        const fontSize = Math.max(Math.floor(item.height * scale * 0.5), 10) + this.fontSizeAdjust
        
        // 文字宽度可能比原始框稍宽，向左稍微调整起始位置以居中显示
        const textWidthAdjust = -5; // 向左微调以补偿文字宽度
        
        // 调整X坐标的偏移量，加上用户设置的额外偏移量
        const adjustedX = item.x * scale + offsetX + this.textOffsetX + textWidthAdjust
        const adjustedY = item.y * scale + offsetY + this.textOffsetY
        
        const adjustedItem = {
          ...item,
          x: adjustedX,
          y: adjustedY,
          width: item.width * scale,
          height: item.height * scale,
          str: item.str,
          fontSize: `${fontSize}px`
        }
        this.ocrResult.push(adjustedItem)
      })
      
      console.log('调整后的OCR结果:', this.ocrResult)
    },
    reprocessOcrResults() {
      // 清空当前结果
      this.ocrResult = []
      
      // 重新处理原始结果
      this.processOcrResults(this.originalOcrResults)
    },
    updateTextStyle() {
      // 更新所有文本项的样式
      document.documentElement.style.setProperty('--bg-opacity', this.bgOpacity);
    },
    quickAdjustRight(amount) {
      this.textOffsetX += amount;
      this.reprocessOcrResults();
    },
    resetAdjustments() {
      this.textOffsetX = 20;
      this.textOffsetY = 0;
      this.reprocessOcrResults();
    },
    copyTextToClipboard() {
      const textToCopy = this.textOutput;
      const tempInput = document.createElement('input');
      tempInput.value = textToCopy;
      document.body.appendChild(tempInput);
      tempInput.select();
      document.execCommand('copy');
      document.body.removeChild(tempInput);
      this.$message.success('文本已复制到剪贴板');
    },
    toggleTableMode() {
      this.tableMode = !this.tableMode;
      // 如果开启表格模式，自动调整为合适的值
      if (this.tableMode) {
        this.bgOpacity = 0.9;
        this.updateTextStyle();
      }
    }
  },
  created() {
    // 初始化CSS变量
    document.documentElement.style.setProperty('--bg-opacity', this.bgOpacity);
  }
}
</script>

<style lang="scss" scoped>
.app-container {
  background-color: #f5f7fa;
  padding: 20px;
}

.engine-tabs {
  margin-bottom: 20px;
  background-color: #fff;
  padding: 15px;
  border-radius: 4px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.ocr-container {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.api-doc-section {
  background-color: #fff;
  padding: 20px;
  border-radius: 4px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

.ocr-usage-section {
  background-color: #fff;
  padding: 20px;
  border-radius: 4px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

h1 {
  font-size: 24px;
  margin-bottom: 20px;
  color: #303133;
}

h2 {
  font-size: 18px;
  margin: 15px 0 10px 0;
  color: #606266;
}

p {
  font-size: 16px;
  margin-bottom: 10px;
  line-height: 1.5;
}

pre {
  background-color: #f9f9f9;
  padding: 10px;
  border: 1px solid #ddd;
  white-space: pre-wrap;
  word-wrap: break-word;
  margin-bottom: 15px;
  line-height: 1.6;
  font-size: 14px;
  border-radius: 4px;
}

.note {
  color: #555;
  font-style: italic;
  margin-top: 10px;
}

code {
  background-color: #f1f1f1;
  padding: 2px 4px;
  border-radius: 3px;
  font-family: Consolas, Monaco, 'Andale Mono', monospace;
}

/* OCR使用部分样式 */
.upload-section {
  text-align: center;
  margin-bottom: 30px;
}

.upload-box {
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
}

.result-section {
  margin-top: 20px;
}

.result-title {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 15px;
  color: #409EFF;
}

/* 调整区域优化 */
.adjust-controls {
  display: flex;
  flex-direction: column;
  margin-bottom: 20px;
  background-color: #f8f9fa;
  padding: 15px;
  border-radius: 4px;
  border: 1px dashed #dcdfe6;
}

.quick-adjust {
  display: flex;
  gap: 10px;
  margin-bottom: 15px;
  flex-wrap: wrap;
}

.adjust-options {
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.adjust-item {
  display: flex;
  flex-direction: column;
}

.adjust-item span {
  margin-bottom: 5px;
  font-weight: bold;
  color: #606266;
}

/* 放大图片展示区域 */
.image-preview-section {
  width: 100%;
  margin-bottom: 20px;
}

.image-container {
  position: relative;
  border: 1px solid #dcdfe6;
  overflow: hidden;
  height: 700px; /* 放大高度 */
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 20px;
  background-color: #f5f7fa;
  border-radius: 4px;
}

.ocr-image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.ocr-result-container {
  position: relative;
}

.ocr-text-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.ocr-text-item {
  position: absolute;
  background-color: rgba(0, 0, 0, var(--bg-opacity, 0.5));
  color: #FFFFFF;
  padding: 1px 2px;
  font-size: 12px; /* 默认字体大小再减小为12px */
  font-weight: 500; /* 增加字重 */
  box-sizing: border-box;
  display: inline-block;
  text-align: left;
  text-shadow: 0px 0px 1px rgba(0, 0, 0, 0.5); /* 添加文字阴影增强可读性 */
  white-space: nowrap;
  overflow: visible;
  line-height: 1.2;
  border-radius: 1px;
  letter-spacing: 0.5px; /* 减小字母间距 */
  -webkit-font-smoothing: antialiased; /* 优化字体渲染 */
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); /* 添加微弱阴影增强层次感 */
}

/* 表格模式样式 */
.table-mode .ocr-text-layer {
  background-color: rgba(255, 255, 255, 0.8);
}

.table-cell {
  border: 1px solid #333 !important;
  background-color: rgba(0, 0, 0, 0.75) !important;
  font-weight: 600 !important;
  padding: 2px 3px !important;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important;
  border-radius: 0 !important;
  text-align: center !important;
}

.is-active {
  background-color: #67C23A;
  border-color: #67C23A;
  color: white;
}

.full-text-section {
  margin-top: 20px;
  padding: 20px;
  background-color: #fff;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  border: 1px solid #ebeef5;
}

@media (max-width: 768px) {
  .ocr-container {
    flex-direction: column;
  }
  
  .image-container {
    height: 500px;
  }
  
  .quick-adjust {
    flex-direction: column;
  }
}
</style>
  
  